Пишем простое приложение с использованием GoogleMap и прокачанным SimpleCursorAdapter / Хабр 您所在的位置:网站首页 android mapview Пишем простое приложение с использованием GoogleMap и прокачанным SimpleCursorAdapter / Хабр

Пишем простое приложение с использованием GoogleMap и прокачанным SimpleCursorAdapter / Хабр

#Пишем простое приложение с использованием GoogleMap и прокачанным SimpleCursorAdapter / Хабр| 来源: 网络整理| 查看: 265

Разработка приложений под платформу Android становится все более и более массовой. Так, мне недавно довелось заниматься разработкой клиентской части такого приложения, фактически с нуля разбираясь в премудростях этого дела, опираясь лишь на базовые знания Java.

Здесь на примере простого приложения, которое позволяет выбрать станцию метро из списка и отобразить её на карте, показаны некоторые полезные фичи работы с адаптером для ListView, реализованы простейшая работа гуглокарты и взаимодействие с встроенной БД. Весь проект можно скачать на github по ссылке в конце статьи.

Собственно, так выглядит список: image

Он включает в себя сам список станций и строку поиска по станциям. В поиске реализована подсказка — при наборе последовательности букв в списке остаются только те станции, название которых начинается с этой последовательности. Тапаем по станции Белорусская, получаем:

image Теперь посмотрим, что же там происходит. Нетрудно заметить, что здесь используется гуглокарта (MapView). Соответственно, важно помнить, что в качестве target при создании проекта нужно указать Google APIs.

Теперь по пунктам:

1. AndroidManifest

Это базовый файл, в котором описываются настройки приложения и указывается, к чему оно имеет доступ. Соответственно, в этом файле необходимо прописать доступ к интернету, указать, что используется библиотека com.google.android.maps, и указать наши активити: в данном случае SearchDialog, которое появляется при запуске, и Map — активити с картой.

2. База данных

Для удобной работы с базой данных необходимо создать database helper, который реализует методы, необходимые нам для работы с БД – такие, как фильтрация по первым буквам и поиск по id. Сам файл RecordsDbHelper.java, как уже говорилось, можно посмотреть по ссылке ниже. Здесь стоит отметить лишь, что мы реализовали методы поиска по id и по текстовому фильтру, которые будем в дальнейшем использовать.

public Cursor getById(long id) { Cursor ccc = mDb.query(true, DATABASE_TABLE, new String[] { KEY_ROWID, KEY_NAME, KEY_X, KEY_Y, KEY_LINES }, KEY_ROWID + "=" + Long.toString(id), null, null, null, null, null); return ccc; } synchronized public Cursor fetchRecordsByQuery(String query) { Cursor ccc = mDb.query(true, DATABASE_TABLE, new String[] { KEY_ROWID, KEY_NAME, KEY_LINES }, KEY_NAME + " LIKE" + "'" + query + "%'", null, null, null, KEY_NAME, null); return ccc; }​ В общем, тут всё как обычно, никаких премудростей.

3. Адаптер

Это класс, реализующий связь списка с Базой данных. В результате запроса к БД мы получаем курсор таблицы. Адаптер берет курсор, список, названия полей таблицы и id элементов из item.xml (см.ниже), которые нужно соотнести друг с другом, и заполняет ListView. Наследуемся от стандартного SimpleCursorAdapter, в котором всё это реализовано, и переопределяем в нём метод getView.

Adapter.java public class Adapter extends SimpleCursorAdapter{ public Adapter(Context context, int layout, Cursor c, String[] from, int[] to) { super(context, layout, c, from, to); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); long id=getItemId(position); view.setTag(id); return view; } } В методе getView готовому View элемента списка(т.е. строке списка с названием станции и цветным кружочком) в тэг(который есть у каждого объекта типа View) помещается id строки таблицы БД, которой этот элемент соответствует. Этот id впоследствии будет передаваться в активити с картой при тапе по элементу списка.

4. Список

Сначала нужно задать его Layout – а именно каркас визуального представления необходимых нам элементов:

search.xml

Layout отдельного элемента списка (картинка – кружочек с цветом линии метро и название станции):

item.xml

Теперь, наконец, класс Search, в котором описывается активити с поиском. Он реализован с помощью наследования от ListActivity – специального активити для списков. Данные о станциях (название, координаты и номер ветки метро) берутся из вышерассмотренной базы данных.

public class Search extends ListActivity { //объявляем переменные private EditText searchText; private RecordsDbHelper recordsDBHelper; private ListView list; private Adapter notes; //вложенный класс Binder описан ниже private Binder binder; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.search); binder = new Binder(); list = getListView(); //устанавливаем кликабельность на нашем списке и указываем обработчик кликов, //который при тапе с помощью intent передаёт id записи, который мы поместили в //тэг(см. выше), в активити с картой и осуществляет переход на него. list.setClickable(true); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView av, View v, int pos, long id) { Intent intent = new Intent(); intent.putExtra("ID", (Long) v.getTag()); intent.setClass(Search.this, Map.class); startActivity(intent); } }); searchText = (EditText) findViewById(R.id.search_text); //указываем обработчик изменения введенного к поле фильтра текста, который при //срабатывании вызывает метод updateList(), описанный в этом классе ниже searchText.addTextChangedListener(new TextWatcher() { public void afterTextChanged(Editable s) { } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { updateList(searchText.getText().toString()); } }); //здесь создаётся объект нашего Database Helper’а, с помощью которого мы //работаем с базой. Операция открытия БД довольно ресурсоёмкая, поэтому //выполняем её в новом отдельном потоке. list.post(new Runnable() { @Override public void run() { recordsDBHelper = new RecordsDbHelper(Search.this); recordsDBHelper.open(); updateList(searchText.getText().toString()); } }); } //Вложенный класс Binder, реализующий интерфейс SimpleCursorAdapter.ViewBinder. //Подробнее о нём написано ниже. Вкратце, здесь в соответствие номеру ветки станции //ставится в соответствие кружочек с нужным цветом. private static class Binder implements Adapter.ViewBinder { public boolean setViewValue(View view, Cursor c, int i) { if (view.getId() == R.id.line_image) { Integer line = c.getInt(i); switch (line) { case 0: ((ImageView) view).setImageResource(R.drawable.line0); break; case 1: ((ImageView) view).setImageResource(R.drawable.line1); break; … … } return true; } return false; } } //В этом методе происходит инициализация адаптера в соответствии с введенным в //фильтре текстом. private void updateList(String s) { if (recordsDBHelper.isReady()) { //вытягиваем курсор на таблицу с отфильтрованными данными Cursor c = recordsDBHelper.fetchRecordsByQuery(s); startManagingCursor(c); //определяем множество полей таблицы, которые нам нужны для заполнения списка String[] from = new String[] { RecordsDbHelper.KEY_NAME, RecordsDbHelper.KEY_LINES }; //и указываем id элементов ячейки списка(т.е. ImageView, в котором будет //цветной кружочек, и TextView, в котором будет название станции) int[] to = new int[] { R.id.text1, R.id.line_image }; //передаём всё это в конструктор адаптера notes = new Adapter(Search.this, R.layout.item, c, from, to); notes.setViewBinder(binder); setListAdapter(notes); } } }

Самое интересное здесь, как мне кажется, Binder. Он позволяет определить, каким образом привязывать значение из таблицы БД к элементу строки списка, то есть, например, как привязать номер ветки, который хранится в таблице, к соответствующему рисунку с кружочком нужного цвета. Метод setViewValue вызывается для каждого view, указанного в конструкторе курсора. Если возвращаемый результат true, то данные привязаны, всё хорошо. Если false — система понимает, что вызван не подходящий метод и ищет нужный среди стандартных Binder’ов. В этом примере проверяется, является ли view, переданный в качестве параметра, тем самым ImageView, в котором должен быть кружочек. Если это так, то передаем туда соответствующую картинку, иначе возвращаем false, дав понять, что нужно использовать стандартный Binder (т.к. это не картинка, а тут мы обрабатываем только картинки).

При вызове активити первым делом вызывается метод onCreate(), в котором инициализируются все отображаемые элементы. Здесь устанавливается обработчик кликов по элементам списка, который вытягивает тэг с id записи и передаёт этот тэг в активити с картой (ну и собственно вызывает это активити). Также устанавливается обработчик изменения текста в EditText (строка фильтрации). При изменении текста вызывается функция updateList(), в которой с помощью описанного выше хелпера кидается запрос в базу, вытаскивающий отфильтрованные данные, и создается адаптер для списка.

5. Карта

Наконец переходим к самим картам. Начнем с Layout:

map.xml

Здесь требуется указать ключ к GoogleMaps API. Свой я светить не стал :) О том, как его получить, можно почитать тут. Ну и собственно активити с картой:

Map.java

public class Map extends MapActivity { public MapView mapView; public MapController mapController; private RecordsDbHelper mDbHelper; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.map); //инициализируем карту mapView = (MapView) findViewById(R.id.mapview); //добавляем стандартные кнопочки зума mapView.setBuiltInZoomControls(true); mapController = mapView.getController(); //здесь открытие БД также сделано в отдельном потоке mapView.post(new Runnable() { @Override public void run() { //открывается база mDbHelper = new RecordsDbHelper(Map.this); mDbHelper.open(); //извлекается доп. инфа, то есть id станции в БД, переданный при тапе по списку Bundle extras = Map.this.getIntent().getExtras(); if (extras != null) { long str = extras.getLong("ID"); //по этому id вытягивается соответствующая запись из БД Cursor cursor = mDbHelper.getById(str); startManagingCursor(cursor); cursor.moveToFirst(); double x = cursor.getDouble(cursor.getColumnIndex(RecordsDbHelper.KEY_X)); double y = cursor.getDouble(cursor.getColumnIndex(RecordsDbHelper.KEY_Y)); //создаём GeoPoint с координатами извлеченными из БД с //помощью курсора GeoPoint point = new GeoPoint((int) (x * 1E6), (int) (y * 1E6)); //и передвигаем карту в эту точку mapController.animateTo(point); mapController.setZoom(16); } } }); } }

Здесь: 1. На карту устанавливаются стандартные кнопки ZoomIn и ZoomOut (с помощью setBuiltInZoomControls()) 2. Открывается наша БД. 3. Из intent, который вызвал это активити, вытягивается id записи, который передавался в обработчике клика по элементу списка (я писал об этом выше), и по этому id из базы достается нужная строчка, из которой нам нужны координаты станции. 4. Координаты вытаскиваются с помощью метода getDouble, полученного в результате запроса. 5. Далее создаётся GeoPoint и выполняются преобразования координат (в базе хранятся дробные, а гуглу нужны целочисленные. для этого нужно взятые из базы умножить на 10^6 и округлить). 6. animateTo() и setZoom() сдвигают карту в нужное место и устанавливают уровень зума.

Фух, в общем всё. Как видно, за достаточно простым функционалом стоят не совсем тривиальные вещи, разобраться с которыми новичку не так просто. Поэтому я очень надеюсь, что опыт, которым я поделился, поможет кому-то не потерять энтузиазм из-за проблем на старте, и качественных приложений, оптимизирующих нашу жизнь, будет больше!

Весь проект можно найти по ссылке.



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有